#![allow(warnings)]
use std::{sync::{Arc, Mutex}, rc::Rc, cell::RefCell};

use jvm_rust::raw;

use crate::{classfile::{self, REF_INVOKE_STATIC, REF_INVOKE_VIRTUAL}, rtda::{Frame, Object, Thread, OperandStack, heap::string_pool, LocalVars, self}, instructions::base::{invoke_java_method, invoke_java_method_log}};

use super::{ConstantPool, Constant, ClassLoader, Class};

// 类符号引用

#[derive(Clone)]
pub struct InvokeDynamic {
    cp: Arc<ConstantPool>,
    // class_name: String, // 类的完全限定名
    // class: Option<Arc<Class>>, // 缓存解析后的类结构体
    // cp_symref ↑
    name: String,
    descriptor: String,
    // cp_memberref ↑
    method_handle: MethodHandle, // 存储method_handle常量 此处methodhandle暂不创建直接运行所指向的方法，后续统一调整。
    arguments: Vec<Option<Constant>>, // bootstrap_method参数
    call_site: Option<*mut Object>, // 保存的调用点
}

// cp_memberref
impl InvokeDynamic {
    // 从class文件内存储的字段或方法常量中提取数据
    pub fn copy_member_ref_info(&mut self, info: &classfile::ConstantInvokeDynamicInfo) {
        // self.class_name = ref_info.class_name();
        let (name, descriptor) = info.name_and_descriptor();
        self.name = name;
        self.descriptor = descriptor;
    }

    pub fn get_name(&self) -> &str {
        &self.name
    }

    pub fn get_descriptor(&self) -> &str {
        &self.descriptor
    }

    pub fn get_method_handle(&self) -> MethodHandle {
        self.method_handle.clone()
    }

    pub fn get_arguments(&self) -> Vec<Option<Constant>> {
        self.arguments.clone()
    }
}

impl InvokeDynamic {
    pub fn new_invoke_dynamic(rt_cp: Arc<ConstantPool>, info: &classfile::ConstantInvokeDynamicInfo) -> InvokeDynamic {
        let class = rt_cp.get_class();
        let cf_cp = info.get_constant_pool();
        let bootstrap_method = class.get_bootstrap_method(info.bootstrap_method_attr_index()).expect("bootstrap_method is none");
        // 获取bootstrap_method引用
        let constant_method_handle = cf_cp.borrow().get_method_handle(bootstrap_method.bootstrap_method_ref as usize).expect("method_handle is none");
        let method_handle = MethodHandle::new(rt_cp.clone() ,&constant_method_handle);

        // 获取bootstrap_method参数
        let arguments: Vec<Option<Constant>> = bootstrap_method.bootstrap_arguments
            .into_iter()
            .map(|i| Constant::new(cf_cp.borrow().get_constant_info(i as usize), rt_cp.clone()))
            .collect();

        let (name, descriptor) = info.name_and_descriptor();

        InvokeDynamic {
            cp: rt_cp,
            name, 
            descriptor, 
            method_handle,
            arguments,
            call_site: None,
        }
    }

    pub fn resolved(&mut self, frame: Rc<RefCell<Frame>>) -> Option<*mut Object> {
        let j_thread = frame.borrow().get_thread().lock().unwrap().get_j_thread();

        // 以解析直接返回
        if self.call_site.is_some() {
            return self.call_site.clone();
        }
        // 解析调用点

        let current_class = frame.borrow().get_method().get_class();
        let loader = current_class.get_class_loader().unwrap();

        // lookup
        let lookup_obj = Self::create_lookup(loader.clone(), current_class.clone(), j_thread);
        assert!(lookup_obj.is_some());
        // 参数
        // invokedName
        let invoke_name = string_pool::j_string(&mut loader.lock().unwrap(), &self.name);

        // invokedType
        let invoked_type = MethodType::create_method_type(loader.clone(), &self.descriptor, j_thread);
        assert!(invoked_type.is_some());

        // samMethodType
        let a = self.arguments[0].as_ref().unwrap().method_type();
        let sam_method_type = MethodType::create_method_type(loader.clone(), &a.descriptor, j_thread);
        assert!(sam_method_type.is_some());

        // implMethod
        let b = self.arguments[1].as_ref().unwrap().method_handle();
        let impl_mehtod = b.create_method_handle(loader.clone(), lookup_obj.clone().unwrap(), current_class.clone(), j_thread);
        assert!(impl_mehtod.is_some());

        // instantiatedMethodType
        let c = self.arguments[2].as_ref().unwrap().method_type();
        let instantiated_method_type = MethodType::create_method_type(loader.clone(), &c.descriptor, j_thread);
        assert!(instantiated_method_type.is_some());
        
        // self.methodhandle
        let metafactory_mh = self.method_handle.create_method_handle(loader.clone(), lookup_obj.clone().unwrap(), current_class.clone(), j_thread).unwrap();
        // assert!(method_handle.is_some());
        let mh_class = unsafe{ &*metafactory_mh }.get_class();
        let invoke_exact = mh_class.get_instance_method("invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;").unwrap();
        
        //
        let mut vars = LocalVars::new_local_vars(invoke_exact.get_max_locals());
        // self
        vars.set_ref(0, Some(metafactory_mh));
        
        let mut obj_arr = Self::create_object_array(loader, 6);
        if let rtda::heap::ArrayObject::Object(j_args) = obj_arr.array() {
            // MethodHandles.Lookup caller
            j_args[0] = lookup_obj;
            // String invokedName
            j_args[1] = Some(invoke_name);
            // MethodType invokedType
            j_args[2] = invoked_type;
            // MethodType samMethodType
            j_args[3] = sam_method_type;
            // MethodHandle implMethod
            j_args[4] = impl_mehtod;
            // MethodType instantiatedMethodType
            j_args[5] = instantiated_method_type;
        }
        // 参数数组
        vars.set_ref(1, Some(raw!(obj_arr)));

        let stack = invoke_java_method(invoke_exact, vars, Some(current_class), j_thread);
        
        let call_site = stack.borrow_mut().pop_ref();
        self.call_site = call_site;
        
        call_site
    }

    fn create_lookup(loader: Arc<Mutex<ClassLoader>>, caller: Arc<Class>, j_thread: Option<*mut Object>) -> Option<*mut Object> {
        let mhs_class = loader.lock().unwrap().load_class("java/lang/invoke/MethodHandles".to_string());
        let lookup_mehtod = mhs_class.get_static_method("lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;").unwrap();
        
        let vars = LocalVars::new_local_vars(lookup_mehtod.get_max_locals());

        // 执行lookup方法,接收返回结果
        let stack = invoke_java_method(lookup_mehtod, vars, Some(caller), j_thread);

        let lookup_obj = stack.borrow_mut().pop_ref();
        lookup_obj
    }

    fn create_object_array(loader: Arc<Mutex<ClassLoader>>, len: usize) -> Object {
        let string_class = loader.lock().unwrap().load_class("java/lang/Object".to_string());
    
        let arr_class = string_class.array_class();
        let obj_arr = Class::new_array(arr_class, len);

        obj_arr
    }


}

#[derive(Clone)]
pub struct MethodType {
    pub descriptor: String
}

impl MethodType {
    pub fn new(info: &classfile::ConstantMethodTypeInfo) -> MethodType {
        let cf_cp = info.get_constant_pool();
        let descriptor = cf_cp.borrow().get_utf8(info.get_descriptor_index());
        MethodType {
            descriptor
        }
    }

    pub fn create_method_type(loader: Arc<Mutex<ClassLoader>>, descriptor: &str, j_thread: Option<*mut Object>) -> Option<*mut Object> {
        let mt_class = loader.lock().unwrap().load_class("java/lang/invoke/MethodType".to_string());
        // public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader) throws IllegalArgumentException, TypeNotPresentException
        let method = mt_class.get_static_method("fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;").unwrap();
        let descriptor_obj = string_pool::j_string(&mut loader.lock().unwrap(), descriptor);
        
        // 传入方法参数
        let mut vars = LocalVars::new_local_vars(method.get_max_locals());
        // descriptor
        vars.set_ref(0, Some(descriptor_obj));
        // loader
        vars.set_ref(1, None);

        // 返回结果
        let stack = invoke_java_method(method, vars, None, j_thread);

        let mt_obj = stack.borrow_mut().pop_ref();
        mt_obj
    }
}
#[derive(Clone)]
pub struct MethodHandle {
    cp: Arc<ConstantPool>,
    kind: u8, // method_handle引用类型，暂不判断
    method_handle: Option<Constant>, // 存储method_handle引用
}

impl MethodHandle {
    pub fn new(rt_cp: Arc<ConstantPool>, info: &classfile::ConstantMethodHandleInfo) -> MethodHandle {
        let cf_cp = info.get_constant_pool();
        let method_handle = Constant::new(cf_cp.borrow().get_constant_info(info.reference_index), rt_cp.clone());
        MethodHandle {
            cp: rt_cp,
            kind: info.reference_kind,
            method_handle
        }
    }

    pub fn get_method_handle(&self) -> Option<Constant> {
        self.method_handle.clone()
    }

    fn create_method_handle(&self, loader: Arc<Mutex<ClassLoader>>, lookup: *mut Object, caller: Arc<Class>, j_thread: Option<*mut Object>) -> Option<*mut Object> {
        // let current_class = frame.get_method().get_class();
        let lookup_class = unsafe { &*lookup }.get_class();
        match self.kind {   
            REF_INVOKE_STATIC => {
                let find_static_method = lookup_class.get_instance_method("findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;").unwrap();

                if let Some(Constant::MethodRef(mr)) = &self.method_handle {
                    let class = mr.lock().unwrap().resolved_class();
                    let method = mr.lock().unwrap().resolved_method();

                    let mut vars = LocalVars::new_local_vars(find_static_method.get_max_locals());
                    // self
                    vars.set_ref(0, Some(lookup));
                    // refc
                    vars.set_ref(1, class.get_j_class());
                    // name
                    let name = string_pool::j_string(&mut loader.lock().unwrap(), &method.get_name());
                    vars.set_ref(2, Some(name));
                    // type
                    let method_type = MethodType::create_method_type(loader, &method.get_descriptor(), j_thread);
                    vars.set_ref(3, method_type);
                    
                    let stack = invoke_java_method(find_static_method, vars, Some(caller), j_thread);

                    let mh_obj = stack.borrow_mut().pop_ref();
                    return mh_obj;
                }
                None
            },
            REF_INVOKE_VIRTUAL => {
                let find_virtual_method = lookup_class.get_instance_method("findVirtual", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;").unwrap();
                if let Some(Constant::MethodRef(mr)) = &self.method_handle {
                    let class = mr.lock().unwrap().resolved_class();
                    let method = mr.lock().unwrap().resolved_method();

                    let mut vars = LocalVars::new_local_vars(find_virtual_method.get_max_locals());
                    // self
                    vars.set_ref(0, Some(lookup));
                    // refc
                    vars.set_ref(1, class.get_j_class());
                    // name
                    let name = string_pool::j_string(&mut loader.lock().unwrap(), &method.get_name());
                    vars.set_ref(2, Some(name));
                    // type
                    let method_type = MethodType::create_method_type(loader, &method.get_descriptor(), j_thread);
                    vars.set_ref(3, method_type);
                    
                    let stack = invoke_java_method(find_virtual_method, vars, Some(caller), j_thread);

                    let mh_obj = stack.borrow_mut().pop_ref();
                    return mh_obj;
                }
                None
            },
            _ => panic!("TODO 暂不支持其他类型: {}" ,self.kind)
        }
    }
}